import { Injectable, Logger } from '@nestjs/common';
import { WHITELIST_RULES, WhitelistRule } from 'src/util/constant';

@Injectable()
export class WhitelistService {
  private readonly logger = new Logger(WhitelistService.name);
  private readonly compiledRules: Map<
    string,
    { rule: WhitelistRule; regex?: RegExp }
  > = new Map();

  constructor() {
    this.compileRules();
  }

  /**
   * 预编译正则表达式规则，提高运行时性能
   */
  private compileRules(): void {
    WHITELIST_RULES.forEach((rule, index) => {
      const key = `rule_${index}`;
      const compiledRule = { rule };

      if (rule.matchType === 'regex') {
        try {
          compiledRule['regex'] = new RegExp(rule.path);
        } catch (error) {
          this.logger.error(`Invalid regex pattern: ${rule.path}`, error);
        }
      }

      this.compiledRules.set(key, compiledRule);
    });

    this.logger.log(`Compiled ${this.compiledRules.size} whitelist rules`);
  }

  /**
   * 检查请求是否在白名单中
   * @param path 请求路径
   * @param method HTTP方法
   * @returns 是否在白名单中
   */
  isWhitelisted(path: string, method: string = 'GET'): boolean {
    const normalizedPath = this.normalizePath(path);
    const upperMethod = method.toUpperCase();

    for (const [, { rule, regex }] of this.compiledRules) {
      if (this.matchesRule(normalizedPath, upperMethod, rule, regex)) {
        this.logger.debug(
          `Whitelist match: ${path} (${method}) -> ${rule.description || rule.path}`,
        );
        return true;
      }
    }

    return false;
  }

  /**
   * 匹配单个规则
   */
  private matchesRule(
    path: string,
    method: string,
    rule: WhitelistRule,
    regex?: RegExp,
  ): boolean {
    // 检查HTTP方法是否匹配
    if (rule.method) {
      const allowedMethods = Array.isArray(rule.method)
        ? rule.method
        : [rule.method];
      if (!allowedMethods.map((m) => m.toUpperCase()).includes(method)) {
        return false;
      }
    }

    // 根据匹配类型进行路径匹配
    switch (rule.matchType) {
      case 'exact':
        return path === rule.path;

      case 'prefix':
        return path.startsWith(rule.path);

      case 'regex':
        return regex ? regex.test(path) : false;

      default:
        this.logger.warn(`Unknown match type: ${rule.matchType}`);
        return false;
    }
  }

  /**
   * 标准化路径，移除查询参数和片段
   */
  private normalizePath(path: string): string {
    try {
      const url = new URL(path, 'http://localhost');
      return url.pathname;
    } catch {
      // 如果不是完整URL，直接处理路径部分
      return path.split('?')[0].split('#')[0];
    }
  }

  /**
   * 获取所有白名单规则（用于调试和管理）
   */
  getRules(): WhitelistRule[] {
    return WHITELIST_RULES;
  }

  /**
   * 添加动态白名单规则（运行时）
   */
  addRule(rule: WhitelistRule): void {
    const key = `dynamic_${Date.now()}`;
    const compiledRule = { rule };

    if (rule.matchType === 'regex') {
      try {
        compiledRule['regex'] = new RegExp(rule.path);
      } catch (error) {
        this.logger.error(`Invalid regex pattern: ${rule.path}`, error);
        throw new Error(`Invalid regex pattern: ${rule.path}`);
      }
    }

    this.compiledRules.set(key, compiledRule);
    this.logger.log(`Added dynamic whitelist rule: ${rule.path}`);
  }
}
